﻿namespace USElections.Utils
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Windows;
    using System.Windows.Data;
    using Microsoft.Phone.Info;

    /// <summary>
    /// General class.
    /// </summary>
    public class General
    {
        #region Fields
        private static readonly int ANIDLength = 32;
        private static readonly int ANIDOffset = 2;
        #endregion

        /// <summary>
        /// Register for notification.
        /// </summary>
        /// <param name="dp">The dependency object.</param>
        /// <param name="propertyName">The property name.</param>
        /// <param name="source">The source object.</param>
        /// <param name="callback">The callback object.</param>
        public static void RegisterForNotification(DependencyObject dp, string propertyName, object source, PropertyChangedCallback callback)
        {
            Binding b = new Binding(propertyName);
            b.Source = source;

            DependencyProperty prop = System.Windows.DependencyProperty.RegisterAttached(
                "ListenAttached" + propertyName,
                typeof(object),
                dp.GetType(),
                new System.Windows.PropertyMetadata(callback));

            BindingOperations.SetBinding(dp, prop, b);
        }

        /// <summary>
        /// Gets the Windows live anonymous Id.
        /// </summary>
        /// <returns>The Windows live anonymous Id.</returns>
        public static string GetWindowsLiveAnonymousID()
        {
            string anid = GetUserPropertyValue<string>("ANID");

            if (anid != null && anid.ToString().Length >= (ANIDLength + ANIDOffset))
            {
                return anid.ToString().Substring(ANIDOffset, ANIDLength);
            }

            return string.Empty;
        }

        /// <summary>
        /// Encrypt a string.
        /// </summary>
        /// <param name="dataToEncrypt">String to encrypt.</param>
        /// <param name="password">The password.</param>
        /// <param name="salt">The salt to use.</param>
        /// <returns>The encrypted string.</returns>
        public static string EncryptA(string dataToEncrypt, string password, string salt)
        {
            AesManaged aes = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));

                aes = new AesManaged();
                aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
                aes.IV = Encoding.UTF8.GetBytes("AAAAAAAAAAAAAAAA"); // rfc2898.GetBytes(aes.BlockSize / 8);

                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);

                byte[] data = Encoding.UTF8.GetBytes(dataToEncrypt);
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();

                var temp = memoryStream.ToArray();
                string result = Convert.ToBase64String(temp);

                return result;
            }
            finally
            {
                if (cryptoStream != null)
                {
                    cryptoStream.Close();
                }

                if (memoryStream != null)
                {
                    memoryStream.Close();
                }

                if (aes != null)
                {
                    aes.Clear();
                }
            }
        }

        /// <summary>
        /// Decrypt a string.
        /// </summary>
        /// <param name="dataToDecrypt">The string to decrypt.</param>
        /// <param name="password">The password.</param>
        /// <param name="salt">The salt to use.</param>
        /// <returns>The decrypted string.</returns>
        public static string DecryptA(string dataToDecrypt, string password, string salt)
        {
            AesManaged aes = null;
            MemoryStream memoryStream = null;
            CryptoStream cryptoStream = null;

            try
            {
                Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));

                aes = new AesManaged();
                aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
                aes.IV = Encoding.UTF8.GetBytes("AAAAAAAAAAAAAAAA"); // rfc2898.GetBytes(aes.BlockSize / 8);

                memoryStream = new MemoryStream();
                cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write);

                byte[] data = Convert.FromBase64String(dataToDecrypt);
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();

                byte[] decryptBytes = memoryStream.ToArray();
                return Encoding.UTF8.GetString(decryptBytes, 0, decryptBytes.Length);
            }
            finally
            {
                if (cryptoStream != null)
                {
                    cryptoStream.Close();
                }

                if (memoryStream != null)
                {
                    memoryStream.Close();
                }

                if (aes != null)
                {
                    aes.Clear();
                }
            }
        }

        private static T GetUserPropertyValue<T>(string key)
        {
            object obj = null;

            if (UserExtendedProperties.TryGetValue(key, out obj))
            {
                try
                {
                    return (T)obj;
                }
                catch
                {
                }
            }

            return default(T);
        }
    }
}
